// -*- c-basic-offset: 2 default-tab-width: 2 indent-tabs-mode: t -*-
// vim: autoindent tabstop=2 noexpandtab shiftwidth=2 softtabstop=2
//===------------ REnvs.h
//
//             This file is part of the reactor project
//                Author: R-Core Team
//
//===---------------------------------------------------------------------===//

#ifndef _ENVIRONMENT_H_
#define _ENVIRONMENT_H_

#include "Array.h"
#include "Symbol.h"
#include "Bindings.h"
#include "Stack.h"
#include "Reactor.h"

namespace R {
	class Environment;
	class REnvs;
	class CollectableEnvironment;
	class FunInfo;
	class EnvStack;

	class Environment : public Any {
	public:
		Environment*	enclosing;// Enclosing function (Lexical scoping)
		FunInfo*			fun;			// Descriptor (throught writeSet)
		Bindings*			bindings;	// Extension frame
		Any*					content[1]; // Flexible part for direct bindings

		virtual void print();

		virtual void inspect() = 0;
		virtual Any* getVar(Symbol *sym, Environment** def, bool* has) = 0;
		virtual void setOrCreateLocalVar(Symbol *sym, Any *value) = 0;
		virtual void createLocalVar(Symbol *sym, Any *value) = 0;
		virtual r_bool exists(Symbol *sym) { nyi_fatal(); }

		static Environment* doNew(Environment* enc, FunInfo *fun);
		static void         doRelease(uint64_t idx);

		int length() { return fun->writeSet.size(); };

		Any* getVarFromWriteSet(int pos, Symbol *sym) {
			R_PARAM(sym);
			R_VAL(Any*, var, content[pos]);
			if(var) 
				return var;
			var = getVarFromReadSet(pos);
			if(var)
				return var;
			return sym->getValue();
		}
		
		Any* getVarFromReadSet(int pos, Symbol *sym) {
			R_PARAM(sym);
			R_VAL(Any*, var, getVarFromReadSet(pos));
			if(var)
				return var;
			return sym->getValue();
		}

		Any* getVarFromTopLevel(Symbol *sym) {
			R_PARAM(sym);
			return sym->getValue();
		}

		void writeAt(int64_t pos, Symbol *sym, Any* val) {
			asSelf(Environment);
			R_PARAM(val);
			R_PARAM(sym);
			self->content[pos] = val;
		}

		static Environment *makeTopLevel(); // FIXME remove this function as soon as I deal nicely with toplevel
	private:
		Any*	getVarFromReadSet(int pos) {
			R_VAR(Any*, val);
			R_VAL(Environment*, cur, enclosing);

			ReadSetEntry rse(fun->readSet.get(pos));
			if(rse.hops)
				return NULL;

			while(--rse.hops)
				cur = cur->enclosing;
			// TODO dirty bit
			if((val = cur->content[rse.position]))
				return val;
			return cur->getVarFromReadSet(rse.position);
		}
	};

	class CollectableEnvironment : public Environment {
		static RVirtualTable* virtualVT;

	public:
		virtual void inspect();

		virtual void tracer(word_t closure);

		virtual Any* getVar(Symbol *sym, Environment** def, bool* has);
		virtual void setOrCreateLocalVar(Symbol *sym, Any *value);
		virtual void createLocalVar(Symbol *sym, Any *value);
		virtual r_bool exists(Symbol *sym);

		static Environment* hidden_doNew(Environment* enc, FunInfo *fun);
		static Environment *makeTopLevel(); // FIXME remove this function as soon as I deal nicely with toplevel
	};

	class EnvInfo {
			friend class EnvStack;
			// Reference to container stack
			EnvStack* stack;
			uint64_t index;
			// Determine if EnvRecord is a gc env or not
			bool isGc;
			// If EnvRecord isGc, env is ptr to Environment obj
			Environment* env_gc;
			// Else EnvRecord points to a non GC obj with embedded bindings.
			// TODO this is a pointer to an alloca env. Have to change it when struct is defined
			uint64_t env_no_gc_idx;

		public:

			EnvInfo(){ stack = NULL; index=-1; env_gc = NULL; env_no_gc_idx = NULL; isGc = false;}

			void set(Environment *env, bool isGC = true){
				env_gc = env;
				isGc = isGC;
			}

			bool isNULL(){
				if(isGc){
					return env_gc == NULL;
				} else {
					nyi_fatal();
				}
				return false;
			}

			/*
			 * Promotes an alloca environment into a collectible environment.
			 */
			Environment* getAsGc() {
				R_VAR(Environment*, res);
				if(isGc) {
					return env_gc;
				} else {
					nyi_fatal();
				}
				return res;
			}

			/*
			 * Push the Target EnvInfo in EnvStack !
			 */
			Any* getVar(Symbol *sym, bool* has, bool pushInfo = false);
			void inspect();
			void print();
			void setOrCreateLocalVar(Symbol *sym, Any *value);
			void createLocalVar(Symbol *sym, Any *value);
			virtual r_bool exists(Symbol *sym);
			void tracer(word_t closure);

			void writeAt(int64_t localPos, Symbol* sym, Any* val) {
				R_PARAM(val);
				R_PARAM(sym);

				printf("local pos %lld, %s, %p, %p\n", localPos, sym->text, env_gc, getAsGc());
				if(localPos >= 0)
					env_gc->writeAt(localPos, sym, val);
				else
					sym->setValue(val);
			}

			void writeAt(int64_t hops, int64_t localPos, Symbol* sym, Any* val) {
				R_PARAM(val);
				R_PARAM(sym);

				printf("local pos %lld, %s, %p, %p\n", localPos, sym->text, env_gc, getAsGc());
				if(hops > 0)
					nyi_fatal();
				else
					sym->setValue(val);
			}

			Any* getVarFromWriteSet(int pos, Symbol *sym) {
				R_PARAM(sym);
				return env_gc->getVarFromWriteSet(pos, sym);
			}
			Any* getVarFromReadSet(int pos, Symbol *sym) {
				R_PARAM(sym);
				return env_gc->getVarFromReadSet(pos, sym);
			}
			Any* getVarFromTopLevel(Symbol *sym) {
				R_PARAM(sym);
				return env_gc->getVarFromTopLevel(sym);
			}
	};

}

#endif /* RENVS_H_ */
